package com.fastwire.carriere.controller.impl;

import java.net.MalformedURLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.ws.Holder;

import org.tmforum.mtop.fmw.xsd.gen.v1.NameAndStringValueType;
import org.tmforum.mtop.fmw.xsd.gen.v1.NameAndValueStringListType;
import org.tmforum.mtop.fmw.xsd.hdr.v1.Header;
import org.tmforum.mtop.nrb.xsd.lay.v1.LayerRateType;
import org.tmforum.mtop.nrb.xsd.lp.v1.LayeredParametersListType;
import org.tmforum.mtop.nrb.xsd.lp.v1.LayeredParametersType;
import org.tmforum.mtop.nrf.xsd.cc.v1.CrossConnectType;
import org.tmforum.mtop.rp.wsdl.conc.v1_0.ConnectionControl;
import org.tmforum.mtop.rp.wsdl.conc.v1_0.ConnectionControlHttp;
import org.tmforum.mtop.rp.wsdl.conc.v1_0.ModifyCrossConnectionException;
import org.tmforum.mtop.rp.xsd.conc.v1.ModifyCrossConnectionRequest;
import org.tmforum.mtop.rp.xsd.conc.v1.ModifyCrossConnectionResponse;

import com.fastwire.carriere.common.Messages;
import com.fastwire.carriere.common.constant.AppConstants;
import com.fastwire.carriere.common.constant.HuaweiInventoryConstants;
import com.fastwire.carriere.common.exception.ResourceException;
import com.fastwire.carriere.common.exception.U2000ProcessException;
import com.fastwire.carriere.common.mapper.NENameMapper;
import com.fastwire.carriere.common.mapper.TPNameMapper;
import com.fastwire.carriere.common.util.CommonLogger;
import com.fastwire.carriere.common.util.EntityUtil;
import com.fastwire.carriere.common.util.HuaweiMTOPUtil;
import com.fastwire.carriere.common.util.StringUtil;
import com.fastwire.carriere.controller.CrossConnectController;
import com.fastwire.carriere.controller.FDFrController;
import com.fastwire.carriere.dao.CrossConnectBandwidthDao;
import com.fastwire.carriere.dao.CrossConnectDao;
import com.fastwire.carriere.dao.DAOException;
import com.fastwire.carriere.dao.DAOFactory;
import com.fastwire.carriere.dao.SystemParameterDao;
import com.fastwire.carriere.hibernate.daoimpl.SystemParameterDaoImpl;
import com.fastwire.carriere.huawei.mtopsi.ra.HuaweiMTOSIConnection;
import com.fastwire.carriere.inventory.entity.CrossConnect;
import com.fastwire.carriere.inventory.entity.CrossConnectBandwidth;
import com.fastwire.carriere.inventory.entity.NetworkElement;
import com.fastwire.carriere.inventory.entity.TransmissionParameter;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multiset;

// TODO: Auto-generated Javadoc
/**
 * The Class BandwidthController.
 */
public class BandwidthControllerImpl {

	/** The sys param dao. */
	private final SystemParameterDao sysParamDao = new SystemParameterDaoImpl();

	/** The cc controller. */
	private final CrossConnectController ccController = new CrossConnectControllerImpl();

	/** The dao factory. */
	private final DAOFactory daoFactory = DAOFactory.getInstance();

	/**
	 * Modify cc.
	 * 
	 * @param bandwidth
	 *            the bandwidth
	 * @param cc
	 *            the cc
	 * @throws U2000ProcessException
	 *             the u2000 process exception
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	private void modifyCC(String bandwidth, CrossConnect cc)
			throws U2000ProcessException, MalformedURLException {

		ModifyCrossConnectionRequest request = new ModifyCrossConnectionRequest();
		request.setCcType(new CrossConnectType());
		Holder<Header> headerHolder = HuaweiMTOSIConnection
				.getSingleResponseHeader();

		CrossConnectType ccType = request.getCcType();
		ccType.setName(EntityUtil.convertNameToNamingAttributeType(cc.getName()));
		ccType.setTransmissionParametersList(new LayeredParametersListType());
		LayeredParametersListType layeredParameterListType = ccType
				.getTransmissionParametersList();
		List<LayeredParametersType> parametersTypes = layeredParameterListType
				.getTransmissionParameters();

		LayeredParametersType parametersType = new LayeredParametersType();
		parametersTypes.add(parametersType);
		LayerRateType mplsLayerRateType = new LayerRateType();
		mplsLayerRateType
				.setValue(HuaweiInventoryConstants.LayerRate_MPLS_Path);
		parametersType.setLayerRate(mplsLayerRateType);

		NameAndValueStringListType nameAndValueStringListType = new NameAndValueStringListType();
		parametersType.setParameterList(nameAndValueStringListType);
		List<NameAndStringValueType> vcsList = nameAndValueStringListType
				.getNvs();
		NameAndStringValueType cir = new NameAndStringValueType();
		cir.setName(HuaweiInventoryConstants.CC_Bandwidth);
		cir.setValue(bandwidth);
		vcsList.add(cir);

		NameAndStringValueType pir = new NameAndStringValueType();
		pir.setName(HuaweiInventoryConstants.CC_PIR);
		pir.setValue(bandwidth);
		vcsList.add(pir);
		{
			NameAndStringValueType cbs = new NameAndStringValueType();
			cbs.setName(HuaweiInventoryConstants.CC_CBS);
			cbs.setValue("0");
			vcsList.add(cbs);
		}
		{
			NameAndStringValueType pbs = new NameAndStringValueType();
			pbs.setName(HuaweiInventoryConstants.CC_PBS);
			pbs.setValue("0");
			vcsList.add(pbs);
		}

		ConnectionControlHttp service = new ConnectionControlHttp(
				HuaweiMTOSIConnection
						.getWebServiceURL(AppConstants.TUNNEL_CONTROL));
		ConnectionControl port = service.getConnectionControlRPC();
		Holder<ModifyCrossConnectionResponse> responseHolder = new Holder<ModifyCrossConnectionResponse>();
		CommonLogger.info(this, "Start to modify Crossconnect");
		try {
			port.modifyCrossConnection(headerHolder, request, responseHolder);
		} catch (ModifyCrossConnectionException e) {
			throw new U2000ProcessException(e.getMessage());
		}

	}

	/**
	 * Gets the total bandwidth port map.
	 * 
	 * @param neList
	 *            the ne list
	 * @param filteredNameList
	 *            the filtered name list
	 * @param multiset
	 *            the multiset
	 * @return the total bandwidth port map
	 * @throws DAOException
	 *             the dAO exception
	 */
	private HashMap<String, String> getTotalBandwidthPortMap(
			Set<String> neList, Collection<String> filteredNameList,
			Multiset<String> multiset) throws DAOException {
		
		HashMap<String, String> bandwidthMap = new HashMap<String, String>();

		CrossConnectBandwidthDao connectBandwidthDao = daoFactory
				.getCrossConnectBandwidthDao();

		CrossConnectDao ccDao = daoFactory.getCrossConnectDao();

		for (String neID : neList) {
			CommonLogger.info(this, "Finding Bw for ");

			//List<CrossConnect> ccList = ccDao.getCrossConnectsbyNE(neID);
			List<CrossConnect> ccList = ccDao.getCrossConnectsbyNE(neID, false);
			List<String> ccNameList = Lists.newArrayList();
			for (CrossConnect ccType : ccList)
			{
				String ccName = ccType.getEmsNativeName();
				ccNameList.add(ccName);
			}
			//Get all CCbandwidth in one go
			HashMap<String, CrossConnectBandwidth> ccBandwidthMap = connectBandwidthDao
			.getCrossConnectBandwidthbyList(ccNameList);
			
			//for (String ccType : ccBandwidthList.keySet()) {
			for (CrossConnect ccType :  ccList) {
				/*
				 CrossConnectBandwidth ccBandwidth = connectBandwidthDao
						.getCrossConnectBandwidthbyName(ccType
								.getEmsNativeName());
				*/
				String ccTypeName = ccType.getEmsNativeName();
				CrossConnectBandwidth ccBandwidth = ccBandwidthMap.get(ccTypeName);
				NetworkElement ne = NENameMapper.getNEbyU2kName(ccType
							.getName());
				int maxBandWidth = 0;

				if (ccBandwidth != null)

					maxBandWidth = ccBandwidth.getMaxbandwidth();

				String ptpKey = null;

				for (int index = 0; index < 2; index++) {

					if (index == 0)
						ptpKey = TPNameMapper.getUniqueKeyforTP(ccType
								.getAendTp().getName(), ne);
					else
						ptpKey = TPNameMapper.getUniqueKeyforTP(ccType
								.getZendTp().getName(), ne);

					if (!StringUtil.isEmptyString(ptpKey)) {
						if (!bandwidthMap.containsKey(ptpKey)) {
							bandwidthMap.put(ptpKey, "0");
						}

						int existingABandthWidthString = Integer
								.parseInt(bandwidthMap.get(ptpKey));
						if (maxBandWidth != -1
								&& !filteredNameList.contains(ccType
										.getEmsNativeName())) {
							existingABandthWidthString += maxBandWidth;
						} else if (filteredNameList.contains(ccType
								.getEmsNativeName())) {
							multiset.add(ptpKey);
						}

						bandwidthMap.put(ptpKey,
								String.valueOf(existingABandthWidthString));
					}

				}

			}

		}

		for (String xx : multiset.elementSet()) {
			CommonLogger.info(this, multiset.count(xx));
		}

		for (String xx : bandwidthMap.keySet()) {
			CommonLogger.info(this, "Port Name:" + xx + ", Bandwidth:"
					+ bandwidthMap.get(xx));
		}
		return bandwidthMap;
	}

	/**
	 * Modify tunnel.
	 * 
	 * @param bandwidth
	 *            the bandwidth
	 * @param ccList
	 *            the cc list
	 * @throws DAOException
	 *             the dAO exception
	 * @throws U2000ProcessException
	 *             the u2000 process exception
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	private void modifyTunnel(String bandwidth, List<CrossConnect> ccList)
			throws DAOException, U2000ProcessException, MalformedURLException {

		CommonLogger.info(this, "total no of cc to activate:" + ccList.size());
		List<CrossConnect> activatedCCList = Lists.newArrayList();

		try {
			for (CrossConnect cc : ccList) {

				TransmissionParameter transParameter = cc
						.getTransmissionParameterbyLR(HuaweiInventoryConstants.LayerRate_MPLS_Path);

				int cir = transParameter.getBandwidth();
				if (cir != 0) {

					modifyCC(bandwidth, cc);
					activatedCCList.add(0, cc);
				}
			}

			for (CrossConnect cc : activatedCCList) {

				TransmissionParameter transParameter = cc
						.getTransmissionParameterbyLR(HuaweiInventoryConstants.LayerRate_MPLS_Path);
				transParameter.setBandwidth(Integer.valueOf(bandwidth));

			}

			daoFactory.getCrossConnectDao().mergeAll(activatedCCList);
		} catch (U2000ProcessException e) {

			CommonLogger.info(this, e.getMessage());
			rollbackCCList(activatedCCList);
			throw e;
		} catch (MalformedURLException e) {
			throw e;
		}

	}

	/**
	 * Change tunnel bandwidth.
	 * 
	 * @param bandwidth
	 *            the bandwidth
	 * @param serviceName
	 *            the service name
	 * @param type
	 *            the type
	 * @throws DAOException
	 *             the dAO exception
	 * @throws U2000ProcessException
	 *             the u2000 process exception
	 * @throws ResourceException
	 *             the resource exception
	 * @throws MalformedURLException
	 *             the malformed url exception
	 */
	public void changeTunnelBandwidth(String bandwidth, String serviceName,
			String type) throws DAOException, U2000ProcessException,
			ResourceException, MalformedURLException {

		/** The fd fr controller. */
		FDFrController fdFrController = new FDFrControllerImpl();

		CommonLogger.info(this, "changeTunnelBandwidth, bandwidth:" + bandwidth
				+ ", serviceName:" + serviceName);

		int bandwidthNum = Integer.valueOf(bandwidth);
		int newBandwidth = bandwidthNum;

		if (HuaweiInventoryConstants.BANDWIDTH_CIR_MAP
				.containsKey(bandwidthNum)) {
			newBandwidth = HuaweiInventoryConstants.BANDWIDTH_CIR_MAP
					.get(bandwidthNum);
		} else {
			newBandwidth = HuaweiMTOPUtil
					.getBandwidthKbpsbySOAFormula(bandwidthNum);
		}

		String tunnelName = serviceName;

		List<String> tunnelList = Lists.newArrayList();

		if (type.equals(AppConstants.ACTIVATION_TYPE_SERVICE)) {
			//use svcname and get fdfr and get fdfr's transmission param 
			//use this param (as CC's name) to get crossconnects 
			//Then get the list of tunnel emsnative name
			tunnelList = fdFrController.getTunnelNamebyServiceId(serviceName);

			if (tunnelList == null || tunnelList.size() == 0) {
				throw new DAOException(Messages.getString(
						"message.crossconnect.noobject.exception",
						new String[] { serviceName }));
			}

			tunnelName = tunnelList.get(0);

		}
		HashMap<String, String> pgNameMap = ccController
				.getProtectionGroupbyTunnelName(tunnelName);

		if (pgNameMap.size() == 0) {
			throw new DAOException(
					"cannot find the protection group associated with serviceName:"
							+ serviceName);
		}

		StringBuffer stringBuffer = new StringBuffer();

		for (String tunnel : tunnelList) {
			stringBuffer.append(tunnel);
			stringBuffer.append(",");
		}

		tunnelList.removeAll(pgNameMap.values());

		if (tunnelList.size() != 0) {
			throw new ResourceException(Messages.getString(
					"message.ebod.tunnelbinding.notsupport.exception",
					new String[] { stringBuffer.toString() }));
		}

		long workingbw = 0;
		long working_rvs_bw = 0;

		LinkedHashMap<String, List<CrossConnect>> newccMap = new LinkedHashMap<String, List<CrossConnect>>();

		newccMap.put(AppConstants.WORKING, daoFactory.getCrossConnectDao()
				.getCrossConnectsbyName(pgNameMap.get(AppConstants.WORKING)));
		newccMap.put(AppConstants.PRT, daoFactory.getCrossConnectDao()
				.getCrossConnectsbyName(pgNameMap.get(AppConstants.PRT)));
		//Get all passing thru NEs
		Set<String> neSet = getAllNEsOfTunnel(newccMap);

		Multiset<String> portMultiset = HashMultiset.create();

		HashMap<String, String> totHashMap = getTotalBandwidthPortMap(neSet,
				pgNameMap.values(), portMultiset);

		newccMap.put(
				AppConstants.WORKING_RVS,
				daoFactory.getCrossConnectDao().getCrossConnectsbyName(
						pgNameMap.get(AppConstants.WORKING_RVS)));
		newccMap.put(AppConstants.PRT_RVS, daoFactory.getCrossConnectDao()
				.getCrossConnectsbyName(pgNameMap.get(AppConstants.PRT_RVS)));

		// bwMap: key = tunnlname , value = bandwidth
		HashMap<String, String> bwMap = Maps.newHashMap();
		for (String key : pgNameMap.keySet()) {
			String ccName = pgNameMap.get(key);
			List<CrossConnect> ccList = newccMap.get(key);
			long bw_tunnel = ccController.getTunnelBandwidth(ccList);

			bwMap.put(ccName, String.valueOf(bw_tunnel));
			if (key.equals(AppConstants.WORKING)) {
				workingbw = bw_tunnel;
			} else if (key.equals(AppConstants.WORKING_RVS)) {
				working_rvs_bw = bw_tunnel;
			}
		}

		List<CrossConnect> newccList = Lists.newArrayList();
		newccList.addAll(newccMap.get(AppConstants.WORKING));

		List<CrossConnect> ccPRTList = newccMap.get(AppConstants.PRT);

		if (ccPRTList != null) {
			if (workingbw < newBandwidth) {

				newccList.addAll(0, ccPRTList);

			} else {
				newccList.addAll(ccPRTList);

			}
		}
		newccList.addAll(newccMap.get(AppConstants.WORKING_RVS));

		List<CrossConnect> ccPRTRvsList = newccMap.get(AppConstants.PRT_RVS);

		if (ccPRTList != null) {
			if (working_rvs_bw < newBandwidth) {

				newccList.addAll(0, ccPRTRvsList);

			} else {
				newccList.addAll(ccPRTRvsList);

			}

		}

		precheckBandwidth(newccList, totHashMap, bwMap, portMultiset,
				newBandwidth);

		modifyTunnel(String.valueOf(newBandwidth), newccList);

	}

	/**
	 * Precheck bandwidth.
	 * 
	 * @param ccList
	 *            the cc list
	 * @param totHashMap
	 *            the tot hash map
	 * @param bandwidthMap
	 *            the bandwidth map
	 * @param portMultiset
	 *            the port multiset
	 * @param newBandwidth
	 *            the new bandwidth
	 * @throws DAOException
	 *             the dAO exception
	 * @throws ResourceException
	 *             the resource exception
	 */
	private void precheckBandwidth(List<CrossConnect> ccList,
			HashMap<String, String> totHashMap,
			HashMap<String, String> bandwidthMap,
			Multiset<String> portMultiset, int newBandwidth)
			throws DAOException, ResourceException {

		for (CrossConnect cc : ccList) {

			NetworkElement ne = NENameMapper.getNEbyU2kName(cc.getName());

			int currentTunnelBandwidth = Integer.valueOf(bandwidthMap.get(cc
					.getEmsNativeName()));

			for (int index = 0; index < 2; index++) {
				String ptp = null;

				if (index == 0) {

					ptp = TPNameMapper.getUniqueKeyforTP(cc.getAendTp()
							.getName(), ne);
				} else {

					ptp = TPNameMapper.getUniqueKeyforTP(cc.getZendTp()
							.getName(), ne);
				}

				if (!StringUtil.isEmptyString(ptp)) {

					int exisitingbandwidth = Integer.valueOf(totHashMap
							.get(ptp)) / 2;

					int totalBandwidth = getBandwidthbyPortName(ptp);

					int tmpBandwidth = newBandwidth;

					// same port is used by working/protection
					if (portMultiset.count(ptp) == 4) {
						tmpBandwidth *= 2;
					}

					if (exisitingbandwidth + tmpBandwidth > totalBandwidth) {
						throw new ResourceException(
								"Not enough bandwidth on Tunnel "
										+ cc.getEmsNativeName()
										+ "\n Current Tunnel Bandwidth:"
										+ currentTunnelBandwidth + "\n NE: "
										+ ne.getEmsNativeName() + "\n Port: "
										+ ptp + "\n Used Bandwidth of Port:"
										+ exisitingbandwidth
										+ "\n New Bandwidth:" + tmpBandwidth
										+ "\n Port Limit:" + totalBandwidth);

					}
				}

			}

		}

	}

	/**
	 * Gets the bandwidthby port name.
	 * 
	 * @param portName
	 *            the port name
	 * @return the bandwidthby port name
	 */
	private int getBandwidthbyPortName(String portName) {

		int bandwidth = 0;
		for (Map.Entry<String, String> entry : AppConstants.PORTCAPACITYMAP
				.entrySet()) {
			if (portName.contains(entry.getKey())) {
				bandwidth = Math.max(bandwidth,
						Integer.valueOf(entry.getValue()));
			}
		}

		CommonLogger.info(this, "portName:" + portName + ", bandwidth:"
				+ bandwidth);

		return bandwidth;
	}

	/**
	 * Gets the all n es of tunnel.
	 * 
	 * @param ccMap
	 *            the cc map
	 * @return the all n es of tunnel
	 */
	private Set<String> getAllNEsOfTunnel(
			HashMap<String, List<CrossConnect>> ccMap) {

		TreeSet<String> neSet = new TreeSet<String>();
		for (List<CrossConnect> ccList : ccMap.values()) {
			for (CrossConnect cc : ccList) {
				neSet.add(NENameMapper.getNEIdbyName(cc.getName()));
			}
		}

		return neSet;
	}

	/**
	 * Rollback cc list.
	 * 
	 * @param ccList
	 *            the cc list
	 */
	private void rollbackCCList(List<CrossConnect> ccList) {
		CommonLogger.info(this, "start rolling back...");
		try {
			for (CrossConnect cc : ccList) {
				// modifyCC(cc.getLayerRate().getBandwidth(), cc);
			}

		} catch (Exception e) {
			CommonLogger.error(this, "Rolling back exception:");
			CommonLogger.error(this, e.getMessage());

		} finally {
			CommonLogger.info(this, "exit rolling back");
		}
	}

}
